using Fusion.Utilities.Generics;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

namespace Fusion.Hotfix.Mono
{
    public class DLLMonoRecorder : MonoBehaviour
    {
        OrderDictionary<string, List<DLLMonoBase>> dllMonoMap = new OrderDictionary<string, List<DLLMonoBase>>();
        private readonly static StringBuilder baseName = new StringBuilder("Fusion.Hotfix.Mono.DLLMonoBase");
        private readonly static string baseNameString = "Fusion.Hotfix.Mono.DLLMonoBase";
        private static StringBuilder monoKeyCache = new StringBuilder(100);
        OrderDictionary<string, string[]> monoClassChainArrayMap = new OrderDictionary<string, string[]>();

        private StringBuilder StartGetMonoKey(string classFullName)
        {
            monoKeyCache.Clear();
            monoKeyCache = GetMonoKey(classFullName);
            return monoKeyCache;
        }

        private StringBuilder GetMonoKey(string classFullName)
        {
            if (baseNameString == classFullName)
            {
                return baseName;
            }
            Type type = DLLManager.Instance.GetType(classFullName);
            return GetMonoKey(type.BaseType.FullName).Append("$").Append(classFullName);
        }

        public bool CheckInMonoClassChain(string target, string hierarchy)
        {
            if (!monoClassChainArrayMap.TryGetValue(hierarchy, out string[] hierarchyArray))
            {
                hierarchyArray = hierarchy.Split("$");
                monoClassChainArrayMap.Add(hierarchy, hierarchyArray);
            }

            for (int i = 0;i < hierarchyArray.Length; i++)
            {
                if (hierarchyArray[i] == target)
                    return true;
            }
            return false;
        }

        public void AddMono(string classFullName, DLLMonoBase dllMono)
        {
            string key = StartGetMonoKey(classFullName).ToString();
            List<DLLMonoBase> list;
            if (!dllMonoMap.TryGetValue(key, out list))
            { 
                list = new List<DLLMonoBase>();
                dllMonoMap[key] = list;
            }
            list.Add(dllMono);
        }

        public void RemoveMono(string classFullName)
        {
            int count = dllMonoMap.Count;
            for (int index = count - 1; index >= 0; index--)
            {
                string key = dllMonoMap.GetKey(index);
                if (CheckInMonoClassChain(classFullName, key))
                {
                    dllMonoMap.Remove(key);
                }
            }
        }

        public void RemoveMono(DLLMonoBase dllMono)
        {
            int count = dllMonoMap.Count;
            for (int index = count - 1; index >= 0; index--)
            {
                List<DLLMonoBase> list = dllMonoMap.Get(index);
                if (list.Remove(dllMono))
                {
                    break;
                }
            }
        }

        public DLLMonoBase GetMono(string classFullName)
        {
            int count = dllMonoMap.Count;
            for (int index = 0; index < count; index++)
            {
                List<DLLMonoBase> list = dllMonoMap.Get(index);
                if (CheckInMonoClassChain(classFullName, dllMonoMap.GetKey(index)) && list.Count > 0)
                {
                    return list[0];
                }
            }

            return null;
        }

        List<DLLMonoBase> list = new List<DLLMonoBase>();

        public DLLMonoBase[] GetMonos(string classFullName)
        {
            list.Clear();
            int count = dllMonoMap.Count;
            for (int index = 0; index < count; index++)
            {
                if (CheckInMonoClassChain(classFullName, dllMonoMap.GetKey(index)))
                {
                    List<DLLMonoBase> monoList = dllMonoMap.Get(index);
                    for (int i = 0; i < monoList.Count; i++)
                    {
                        list.Add(monoList[i]);
                    }
                }
            }
            return list.Count > 0 ? list.ToArray() : null;
        }
    }
}

